package com.xnewtech.xplatform.swan.system.core.dbs.listener;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.log.Log;
import com.alibaba.druid.pool.DruidDataSource;
import com.ulisesbocchio.jasyptspringboot.detector.DefaultPropertyDetector;
import com.ulisesbocchio.jasyptspringboot.exception.DecryptionException;
import com.xnewtech.xplatform.swan.libs.config.properties.DruidProperties;
import com.xnewtech.xplatform.swan.system.core.dbs.context.DataSourceContext;
import com.xnewtech.xplatform.swan.system.core.dbs.exception.DataSourceException;
import com.xnewtech.xplatform.swan.system.core.dbs.exception.enums.DataSourceExceptionEnum;
import com.xnewtech.xplatform.swan.system.core.dbs.factory.DruidFactory;
import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
import org.jasypt.encryption.pbe.config.EnvironmentPBEConfig;
import org.jasypt.exceptions.EncryptionOperationNotPossibleException;
import org.springframework.boot.context.event.ApplicationContextInitializedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.Ordered;
import org.springframework.core.env.ConfigurableEnvironment;


/**
 * 多数据源的初始化，加入到数据源Context中的过程
 *
 * @author xnew
 * @date 2020/8/25
 */
public class MultiDatasourceInitListener implements ApplicationListener<ApplicationContextInitializedEvent>, Ordered {

    private static final Log log = Log.get();

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }

    @Override
    public void onApplicationEvent(ApplicationContextInitializedEvent applicationContextInitializedEvent) {

        ConfigurableApplicationContext applicationContext = applicationContextInitializedEvent.getApplicationContext();

        // 如果是配置中心的上下文就略过
        if (applicationContext instanceof AnnotationConfigApplicationContext) {
            return;
        }

        ConfigurableEnvironment environment = applicationContextInitializedEvent.getApplicationContext().getEnvironment();

        // 获取数据库连接配置
        String dataSourceDriver = environment.getProperty("spring.datasource.driver-class-name");
        String dataSourceUrl = environment.getProperty("spring.datasource.url");
        String dataSourceUsername = environment.getProperty("spring.datasource.username");
        String dataSourcePassword = environment.getProperty("spring.datasource.password");

        // 获取jasypt 加密秘钥 和加密算法  algorithm
        String password = environment.getProperty("jasypt.encryptor.password");
        String algorithm = environment.getProperty("jasypt.encryptor.algorithm");

        // 如果有为空的配置，终止执行
        if (ObjectUtil.hasEmpty(dataSourceUrl, dataSourceUsername, dataSourcePassword)) {
            throw new DataSourceException(DataSourceExceptionEnum.DATA_SOURCE_READ_ERROR);
        }
        if (ObjectUtil.hasEmpty(password)) {
            throw new DataSourceException(DataSourceExceptionEnum.JASRYPT_ENCRYPTOR_ERROR);
        }

        // 创建主数据源的properties
        DruidProperties druidProperties = createMasterDBSourceProperties(password,algorithm,dataSourceDriver,dataSourceUrl,dataSourceUsername,dataSourcePassword);

        // 创建主数据源
        DruidDataSource druidDataSource = DruidFactory.createDruidDataSource(druidProperties);

        // 初始化数据源容器
        try {
            DataSourceContext.initDataSource(druidProperties, druidDataSource);
        } catch (Exception e) {
            log.error(">>> 初始化数据源容器错误!", e);
            throw new DataSourceException(DataSourceExceptionEnum.INIT_DATA_SOURCE_ERROR);
        }

    }

    // 创建主数据源的properties
    private DruidProperties createMasterDBSourceProperties(String jasryptPassword,String jasryptAlgorithm,
                                                           String dataSourceDriver,String dataSourceUrl,String dataSourceUsername,String dataSourcePassword){
        // 创建探测器与加密器
        DefaultPropertyDetector detector = new DefaultPropertyDetector();
        StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
        EnvironmentPBEConfig config = new EnvironmentPBEConfig();
        if (!ObjectUtil.hasEmpty(jasryptAlgorithm)){
            config.setAlgorithm(jasryptAlgorithm);
        }else {
            config.setAlgorithm("PBEWithMD5AndDES");
        }
        config.setPassword(jasryptPassword);
        encryptor.setConfig(config);

        DruidProperties druidProperties = new DruidProperties();
        druidProperties.setDriverClassName(resolvePropertyValue(detector,encryptor,dataSourceDriver));
        druidProperties.setUrl(resolvePropertyValue(detector,encryptor,dataSourceUrl));
        druidProperties.setUsername(resolvePropertyValue(detector,encryptor,dataSourceUsername));
        druidProperties.setPassword(resolvePropertyValue(detector,encryptor,dataSourcePassword));
        return druidProperties;
    }



    // 判断属性值，非加密的返回原值，加密的返回解密值
    private String resolvePropertyValue(DefaultPropertyDetector detector,StandardPBEStringEncryptor standardPBEStringEncryptor,
                                        String value){
        String actualValue = value;
        if (detector.isEncrypted(value)) {
            try {
                String unwrapValue = detector.unwrapEncryptedValue(value.trim());
                actualValue = standardPBEStringEncryptor.decrypt(unwrapValue);
            } catch (EncryptionOperationNotPossibleException e) {
                throw new DecryptionException("Decryption of Properties failed,  make sure encryption/decryption " +
                        "passwords match", e);
            }
        }
        return actualValue;
    }
}
